<template>
  <div class="goods-sku">
    <dl v-for="spec in goods.specs" :key="spec.name">
      <dt>{{ spec.name }}</dt>
      <dd>
        <template v-for="btnObj in spec.values" :key="btnObj.name">
          <!-- 当 btnObj.picture 存在就用图片表示，否则就用文字表示 -->
          <!-- 当选中的时候，用 selected class 进行表示 -->
          <!-- 当禁用的时候，用 disabled class 进行表示 -->
          <img
            :class="{
              // 当 btnObj 有 selected 的时候才选中
              selected: btnObj.selected,
              // 当 btnObj 有 disabled 的时候才禁用
              disabled: btnObj.disabled
            }"
            v-if="btnObj.picture"
            :src="btnObj.picture"
            alt=""
            @click="changeSku(btnObj, spec)"
          />
          <span
            :class="{
              // 当 btnObj 有 selected 的时候才选中
              selected: btnObj.selected,
              // 当 btnObj 有 disabled 的时候才禁用
              disabled: btnObj.disabled
            }"
            v-else
            @click="changeSku(btnObj, spec)"
            >{{ btnObj.name }}</span
          >
        </template>
      </dd>
    </dl>
  </div>
</template>
<script>
import bwPowerSet from '@/vendor/power-set'
const spliter = '🍟'
// 得到字典
const getPathMap = (skus) => {
  // console.log(skus)
  // 得到有效的 sku
  const pathMap = {}
  skus
    .filter((sku) => sku.inventory > 0)
    .forEach((sku) => {
      // 找到每一个 sku 规格下面的数据，生成一个数组
      const specs = sku.specs.map((spec) => spec.valueName)
      // 找 specs 的子集
      const bwSpecs = bwPowerSet(specs)
      // 遍历子集数组，把每一个子集转成字符串当做对象 key，value 是 sku.id
      bwSpecs.forEach((bw) => {
        const key = bw.join(spliter)
        // 如果之前添加过，就继续往里面 push sku.id
        if (pathMap[key]) {
          // 往里面添加
          pathMap[key].push(sku.id)
        } else {
          // 否则，直接作为数组的第一项赋值
          pathMap[key] = [sku.id]
        }
      })
    })
  return pathMap
}

// 遍历所有按钮，判断按钮.name 作为 key，在 pathMap 字典中能否找到，能找到就启动，找不到就禁用
const updateDisabledStatus = (specs, pathMap) => {
  // specs => 所有规格 => 每一行规格
  specs.forEach((spec, i) => {
    // spec.values => 每一行的所有按钮
    spec.values.forEach((btnObj) => {
      /* if (pathMap[btnObj.name]) {
        // 找到了，启用
        btnObj.disabled = false
      } else {
        // 没找到，禁用
        btnObj.disabled = true
      } */
      // 如果当前已经选中了，就没有必要和自己组合了
      if (btnObj.selected) return
      // 拿到原来的选中规格
      const originSelected = getSelectedSpec(specs)
      // 用每一个按钮的 name 进行组合
      originSelected[i] = btnObj.name
      // console.log(originSelected)
      // 去掉 undefined，转成字符串
      const key = originSelected.filter((v) => v).join(spliter)
      // 用转换后的字符串当做 key，看一看在字典中能不能找到
      if (pathMap[key]) {
        btnObj.disabled = false
      } else {
        btnObj.disabled = true
      }
    })
  })
}

// 点击按钮的时候，【找到曾经选择的那些数据】，用选择的数据和其他的规格进行组合，如果组合后的结果在字典中找不到就禁用
// 【找到曾经选择的那些数据】怎么找？从所有的规格的按钮中找 selected 为 true 的
const getSelectedSpec = (specs) => {
  const arr = []
  specs.forEach((spec, i) => {
    // 从每一行规格的按钮中找 selected 为 true
    const btnObj = spec.values.find((btnObj) => btnObj.selected)
    /* if (btnObj) {
      // 如果找到了，就把名字装到数组里面
      arr.push(btnObj.name)
    } */
    if (btnObj) {
      arr[i] = btnObj.name
    } else {
      arr[i] = undefined
    }
  })
  return arr
}

// 从所有的 skus 中找 sku.id 等于传递过来 skuId 的那一项 【sku】
// 遍历每一个规格中的按钮，如果 btnObj.name 等于 【sku】 下 specs 中对应选项的 valueName，则选中
const initDefaultSelected = (goods, skuId) => {
  // 所有的 sku 中找有没有一项的 id 等于传过来的 skuId
  const sku = goods.skus.find((sku) => sku.id === skuId)
  if (!sku) return
  // console.log(sku)
  // 遍历所有个规格
  goods.specs.forEach((spec, i) => {
    spec.values.forEach((btnObj) => {
      btnObj.selected = btnObj.name === sku.specs[i].valueName
    })
  })
}
export default {
  name: 'GoodsSku',
  props: {
    goods: {
      type: Object,
      default: () => ({ specs: [], skus: [] })
    },
    skuId: {
      type: String,
      // 1369155864430120962
      default: ''
    }
  },
  setup(props, context) {
    const pathMap = getPathMap(props.goods.skus)
    // !一上来就调用一下，看一下有没有需要禁用的按钮（规格）
    updateDisabledStatus(props.goods.specs, pathMap)
    // console.log(pathMap)
    // 一上来就根据 skuId 进行一个默认选中的操作
    initDefaultSelected(props.goods, props.skuId)
    const changeSku = (btnObj, spec) => {
      // 当按钮被禁用了，就不再往下进行了
      if (btnObj.disabled) return
      // 点击高亮思路：找到点击的那一项，把那一项的 selected 变为 true 不就选中了吗
      if (btnObj.selected) {
        // 取消选中
        btnObj.selected = false
      } else {
        // 选中，排它
        spec.values.forEach((btnObj) => (btnObj.selected = false))
        btnObj.selected = true
      }

      // const arr = getSelectedSpec(props.goods.specs)
      // console.log(arr)
      // !点击的时候，更新按钮的禁用/启用状态的方法
      updateDisabledStatus(props.goods.specs, pathMap)

      // 如果说都选中了，形成了具体的 sku，就希望把信息传递到父组件
      const selecteSpec = getSelectedSpec(props.goods.specs).filter((v) => v)
      if (selecteSpec.length === props.goods.specs.length) {
        // 根据选中的数据，转成字符串，当做 key，去字典中找 skuId
        const skuId = pathMap[selecteSpec.join(spliter)][0]
        // 根据 skuId 去所有的 skus 里面找具体的 sku
        const sku = props.goods.skus.find((sku) => sku.id === skuId)
        // 把 sku 相关的信息传递到外部组件
        context.emit('change', {
          skuId: sku.id,
          price: sku.price,
          oldPrice: sku.oldPrice,
          inventory: sku.inventory,
          specsText: sku.specs
            .reduce((str, item) => `${str} ${item.name}：${item.valueName}`, '')
            .trim('')
        })
      } else {
        context.emit('change', {})
      }
    }
    return { changeSku }
  }
}
</script>
<style scoped lang="less">
.sku-state-mixin () {
  border: 1px solid #e4e4e4;
  margin-right: 10px;
  cursor: pointer;
  &.selected {
    border-color: @xtxColor;
  }
  &.disabled {
    opacity: 0.6;
    border-style: dashed;
    cursor: not-allowed;
  }
}
.goods-sku {
  padding-left: 10px;
  padding-top: 20px;
  dl {
    display: flex;
    padding-bottom: 20px;
    align-items: center;
    dt {
      width: 50px;
      color: #999;
    }
    dd {
      flex: 1;
      color: #666;
      > img {
        width: 50px;
        height: 50px;
        .sku-state-mixin ();
      }
      > span {
        display: inline-block;
        height: 30px;
        line-height: 28px;
        padding: 0 20px;
        .sku-state-mixin ();
      }
    }
  }
}
</style>
